package top.z.template.parse.ast;

import top.z.template.Context;
import top.z.template.Error;
import top.z.template.Template;
import top.z.template.interpreter.AstInterpreter;
import top.z.template.parse.Ast;
import top.z.template.parse.Ast.Expression;
import top.z.template.parse.Ast.Node;
import top.z.template.parse.Span;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

/**
 * if 条件
 */
public class If extends Node {

    private final List<If>   elseIf;
    private final Expression condition;
    private final List<Node> trueBlock;
    private final List<Node> falseBlock;

    public If(Span span, Expression condition, List<Node> trueBlock, List<If> elseIf, List<Node> falseBlock) {
        super(span);
        this.elseIf = elseIf;
        this.condition = condition;
        this.trueBlock = trueBlock;
        this.falseBlock = falseBlock;
    }

    public Ast.Expression getCondition() {
        return condition;
    }

    public List<Node> getTrueBlock() {
        return trueBlock;
    }

    public List<If> getElseIf() {
        return elseIf;
    }

    public List<Node> getFalseBlock() {
        return falseBlock;
    }

    @Override
    public Object evaluate(Template template, Context context, OutputStream out) throws IOException {
        Object condition = getCondition().evaluate(template, context, out);
        if (!(condition instanceof Boolean)) {
            throw Error.error("Expected a condition evaluating to a boolean, got " + condition, getCondition().getSpan());
        }
        if ((Boolean) condition) {
            context.push();
            Object breakOrContinueOrReturn = AstInterpreter.interpretNodeList(getTrueBlock(), template, context, out);
            context.pop();
            return breakOrContinueOrReturn;
        }

        if (!getElseIf().isEmpty()) {
            for (If elseIf : getElseIf()) {
                condition = elseIf.getCondition().evaluate(template, context, out);
                if (!(condition instanceof Boolean)) {
                    throw Error.error("Expected a condition evaluating to a boolean, got " + condition, elseIf.getCondition().getSpan());
                }
                if ((Boolean) condition) {
                    context.push();
                    Object breakOrContinueOrReturn = AstInterpreter.interpretNodeList(elseIf.getTrueBlock(), template, context, out);
                    context.pop();
                    return breakOrContinueOrReturn;
                }
            }
        }

        if (!getFalseBlock().isEmpty()) {
            context.push();
            Object breakOrContinueOrReturn = AstInterpreter.interpretNodeList(getFalseBlock(), template, context, out);
            context.pop();
            return breakOrContinueOrReturn;
        }
        return null;
    }

}
